﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CPS1
{
    // NUDY: Patrz VaultSignal
    public class GaussianNoise : Signal 
    {
        private Random rand;

        private Double nextRand;

        public GaussianNoise(Double A, Double t1, Double d, Double f, String name) 
        {
            this.A = A;
            this.t1 = t1;
            this.d = d;
            this.f = f;
            this.name = name;
            this.periodical = false;
            this.discrete = false;
            int amount = (int) Math.Ceiling(d * f);
            this.calculatedValues = new Double[amount, 2];
            this.rand = new Random();
            this.nextRand = -1000.0; // gdy nextRand = -1000 oznacza to, że druga wygenerowana wartość została
                                     // pobrana i trzeba pobrać nową wartość
        }

        // korzystamy z transformacji Boxa-Mullera - http://mathworld.wolfram.com/Box-MullerTransformation.html
        override public Double value(Double t)
        {
            // http://stackoverflow.com/questions/629798/problem-with-random-nextgaussian
            if (nextRand == -1000.0) // gdy brak kolejne liczby - generuj kolejną parę
            {
                Double v1, v2, s, multiplier;
                do
                {
                    do
                    {
                        do
                        {
                            v1 = 2 * this.rand.NextDouble() - 1;  
                            v2 = 2 * this.rand.NextDouble() - 1;
                            s = v1 * v1 + v2 * v2;
                        } while (s >= 1 || s == 0);
                        multiplier = Math.Sqrt(-2 * Math.Log(s) / s);
                    } while (v1 * multiplier > 1.0 || v1 * multiplier < -1.0);
                    this.nextRand = v2 * multiplier;
                } while (-this.A > (v1 * multiplier) || (v1 * multiplier) > this.A);
                return v1 * multiplier;
            }
            else // gdy jest dana liczba - zwróć ją i ustaw nextRand na -1000 tak by przy następnej okacji wygenerowano nową parę
            {
                Double temp = this.nextRand;
                this.nextRand = -1000.0;
                return temp;
            }
        }

        public override Signal copy()
        {
            GaussianNoise copied = new GaussianNoise(this.A, this.t1, this.d, this.f, this.name);
            copied.calculateSignal();
            return copied;
        }
    }
}
